<?php

namespace App\Services\ApplyGoldWastesSubsidy;

use Carbon\Carbon;
use App\Models\User;
use App\Models\Order;
use App\Models\Address;
use App\Models\GoldWaste;
use App\Models\ProductSku;
use Brick\Math\RoundingMode;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;

class ApplyGoldWastesOrder
{
    protected User $user;

    protected Address $address;

    protected ?GoldWaste $waste = null;

    /**
     * @var Model|Order|null
     */
    protected ?Model $order = null;

    public function __construct(User  $user, Address $address)
    {
        $this->user = $user;
        $this->address = $address;
    }

    protected function create()
    {
        DB::transaction(function () {
            if (! $this->waste) return;

            $this->order = $this->user->orders()->create([
                'gold_waste_id' => $this->waste->id,
                'paid_at' => $this->waste->created_at,
            ]);

            $this->addAddressToOrder();
            $this->addItemsToOrder();
            $this->order->unsetRelations();
        });

        return $this->order;
    }

    protected function addAddressToOrder()
    {
        $order = $this->order;
        $address = $this->address;
        $order->province = $address->province;
        $order->city = $address->city;
        $order->district = $address->district;
        $order->address = [
            'address' => $address->full_address,
            'contact_name' => $address->contact_name,
            'contact_phone' => $address->contact_phone,
        ];
    }

    protected function items(): array
    {
        return [[
            'sku_id' => $this->waste->product_sku_id,
            'amount' => 1
        ]];
    }

    protected function addItemsToOrder()
    {
        $items = $this->items();
        $orderTotalMoney = big_decimal(0);

        foreach ($items as $item) {
            $itemTotalMoney = $this->createOrderItem($item);
            $orderTotalMoney = $orderTotalMoney->plus($itemTotalMoney);
        }

        $this->updateOrderAfterAddingItem($orderTotalMoney);
    }

    protected function createOrderItem($item)
    {
        $amount = $item['amount'];
        $sku = isset($item['sku_id']) ? ProductSku::findOrFail($item['sku_id']) : sku_from_attr($item['attr_value_index']);

        if ($amount > $sku->stock) fail('商品库存不足，无法创建订单');

        $itemTotalMoney = $this->getItemTotalMoney($sku, $amount);

        $this->order->items()->create([
            'vendor_id' => $sku->product->vendor_id,
            'product_id' => $sku->product_id,
            'product_sku_id' => $sku->id,
            'product_title' => $sku->product->title,
            'product_cover' => $sku->product->cover,
            'sku_name' => $sku->name,
            'sku_pic' => $sku->pic,
            'sku_attr_value_index' => $sku->attr_value_index,
            'sku_attr_value' => $sku->getAttrKeyValue(),
            'amount' => $amount,
            'cost' => $this->getItemSkuCost($sku),
            'total_cost' => $this->getItemSkuTotalCost($sku, $amount),
            'price' => $this->getFinalSkuPrice($sku),
            'total_amount' => $itemTotalMoney,
            'profit' => $this->getItemSkuProfit($sku),
            'total_profit' => $this->getItemSkuTotalProfit($sku, $amount),
        ]);

        if (! $sku->decreaseStock($item['amount'])) fail('商品库存不足，无法创建订单');

        return $itemTotalMoney;
    }

    protected function updateOrderAfterAddingItem($totalMoney)
    {
        $this->order->update([
            'total_amount' => (string)$totalMoney,
            'total_profit' => (string)$this->getOrderProfit(),
            'is_gold_bean' => $this->order->isGoldBeanOrder(),
            'is_gold_coin' => $this->order->isGoldStormOrder(),
        ]);
    }

    protected function getFinalSkuPrice(ProductSku $sku)
    {
        if ($this->waste) {
            return big_decimal($this->waste->record_price);
        } elseif ($this->user->isValidVip()) {
            return big_decimal($sku->price_vip);
        } else {
            return big_decimal($sku->price);
        }
    }

    protected function getItemSkuCost(ProductSku $sku)
    {
        return big_decimal($sku->price_cost);
    }

    protected function getItemSkuTotalCost(ProductSku $sku, $amount)
    {
        $skuCostPrice = big_decimal($sku->price_cost);
        return $skuCostPrice->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
    }

    protected function getItemSkuProfit(ProductSku $sku)
    {
        if ($this->waste) {
            return big_decimal(0);
        } else {
            $skuPrice = $this->getFinalSkuPrice($sku);
            $skuCostPrice = $this->getItemSkuCost($sku);
            return $skuPrice->minus($skuCostPrice);
        }
    }

    protected function getItemSkuTotalProfit(ProductSku $sku, $amount)
    {
        $profit = $this->getItemSkuProfit($sku);
        return $profit->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
    }

    protected function getItemTotalMoney(ProductSku $sku, $amount)
    {
        $skuPrice = $this->getFinalSkuPrice($sku);
        return $skuPrice->multipliedBy($amount)->toScale(2, RoundingMode::HALF_UP);
    }

    protected function getOrderProfit()
    {
        if ($this->waste) {
            return big_decimal(0);
        } else {
            $order = $this->order;
            $total = big_decimal($order->items()->sum('total_amount'));
            $cost = $order->items()->sum('total_cost');
            return $total->minus($cost)->toScale(2, RoundingMode::HALF_UP);
        }
    }

    public function execute()
    {
        $user = $this->user;

        $wastesOrderPeriod = cs('storm')->gold_wastes_order_period;

        if ($wastesOrderPeriod <= 0) return;

        foreach ($user->goldWastes as $waste) {
            $this->waste = $waste;
            $this->create();
            $waste->delete();
        }

        $user->update(['gold_wastes_order_at' => Carbon::today()->addDays($wastesOrderPeriod)]);
    }

    public function apply()
    {
        $this->execute();
    }
}
